#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <omp.h>

typedef struct pixel8 {
	uint8_t r;
	uint8_t g;
	uint8_t b;
	uint8_t a;
} pixel8;

typedef struct pixel32 {
	uint32_t r;
	uint32_t g;
	uint32_t b;
	uint32_t a;
} pixel32;

pixel32 *image, *new_image;
//int16_t width=0, height=0;
int32_t width=0, height=0;

pixel32 smooth(int pos_x, int pos_y) {
	pixel32 out_pixel, temp_pixel;
	uint32_t count=0, index=0, sum_r=0, sum_g=0, sum_b=0, sum_a=0;
	int i,j;

	if ((pos_x<2) || (pos_x>=height-2) || (pos_y<2) || (pos_y>=width-2)){
		for(i=-2;i<=2;i++){
			if ((pos_x+i<0) || (pos_x+i>=height)) continue;
			for(j=-2;j<=2;j++){
				if ((pos_y+j<0) || (pos_y+j>=width)) continue;
				count++;
				temp_pixel = image[(pos_x+i)*width+pos_y+j];
				sum_r += temp_pixel.r;
				sum_g += temp_pixel.g;
				sum_b += temp_pixel.b;
				sum_a += temp_pixel.a;
			}
		}
	}
	else { 
		count=25;
		for(i=-2;i<=2;i++){
			index=(pos_x+i)*width+pos_y;
			for(j=-2;j<=2;j++){
				temp_pixel = image[index+j];
				sum_r += temp_pixel.r;
				sum_g += temp_pixel.g;
				sum_b += temp_pixel.b;
				sum_a += temp_pixel.a;
			}
		}
	}
	out_pixel.r = sum_r/count;
	out_pixel.g = sum_g/count;
	out_pixel.b = sum_b/count;
	out_pixel.a = sum_a/count;

	return out_pixel;
}

int main(int argc, char* argv[]) {
	FILE *input_file=NULL, *output_file=NULL;
	pixel8 *image8, *new_image8;
	int i,j, tmp;
	uint8_t *buffer;
	uint8_t Rbit, Gbit, Bbit, Abit;
	struct timespec start_time, end_time, total_time;
	
	clock_gettime(CLOCK_MONOTONIC, &start_time);

	//Ler entrada
	input_file = fopen("image.in","r");
	output_file = fopen("image.out","w");

	fseek(input_file, 0xA, SEEK_SET);
	fread(&tmp, 4, 1, input_file);

	fseek(input_file, 0x12, SEEK_SET);
	//fread(&width, 2, 1, input_file);
	//fread(&height, 2, 1, input_file);
	fread(&width, 4, 1, input_file);
	fread(&height, 4, 1, input_file);
	
	image = (struct pixel32 *) malloc(width*height*sizeof(struct pixel32));
	image8 = (struct pixel8 *) malloc(width*height*sizeof(struct pixel8));
	new_image = (struct pixel32 *) malloc(width*height*sizeof(struct pixel32));
	buffer = (uint8_t *) malloc(tmp*sizeof(uint8_t));

	fseek(input_file, 0, SEEK_SET);
	fread(buffer, sizeof(uint8_t), tmp, input_file);

	//fseek(input_file, tmp, SEEK_SET);
	fread(image8, sizeof(struct pixel8), width*height, input_file);
	#pragma omp parallel for private(i) shared(image, image8) schedule(guided)
	for (i=0; i<height*width; i++){
		image[i].r = image8[i].r;
		image[i].g = image8[i].g;
		image[i].b = image8[i].b;
		image[i].a = image8[i].a;
	}

	//Aplicar estêncil
	for(i=0; i<height; i++) {
	#pragma omp parallel for private(j) shared(new_image) schedule(guided)
		for(j=0; j<width; j++) {
			new_image[i*width+j] = smooth(i, j);
		}
	}

	//Escrever saída
	//fwrite(&width, 2, 1, output_file);
	//fwrite(&height, 2, 1, output_file);
	fwrite(buffer, sizeof(uint8_t), tmp, output_file);
	#pragma omp parallel for private(i) shared(new_image, image8) schedule(guided)
	for (i=0; i<height*width; i++){
		image8[i].r = (uint8_t) new_image[i].r;
		image8[i].g = (uint8_t) new_image[i].g;
		image8[i].b = (uint8_t) new_image[i].b;
		image8[i].a = (uint8_t) new_image[i].a;
	}
	fwrite(image8, sizeof(struct pixel8), width*height, output_file);
	
	fclose(input_file);
	fclose(output_file);
	//**Necessário? Gasta tempo. Podemos verificar no profiler.
	free(image);
	free(new_image);
	free(image8);
	free(buffer);

	//Calculando tempo total.
	clock_gettime(CLOCK_MONOTONIC, &end_time);
	total_time.tv_sec = end_time.tv_sec - start_time.tv_sec;
	total_time.tv_nsec = end_time.tv_nsec - start_time.tv_nsec;
	if (total_time.tv_nsec < 0) {
		total_time.tv_sec--;
		total_time.tv_nsec+=1.e9;
	}
	printf("Time:%ld.%lds\n",total_time.tv_sec, total_time.tv_nsec);

	return 0;
}
